﻿#region Copyright (c) 2013-04, Olaf Kober <amarok.projects@gmail.com>
//================================================================================
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//================================================================================
#endregion

#if TESTS

using System;
using System.Threading;
using NUnit.Framework;


namespace Amarok.Agents
{
	[TestFixture]
	public class Test_PublisherExtensions
	{
		private class TestMessage : Message
		{
		}

		private class TestOperation : Operation<String, Int32>
		{
		}

		private class MockPublisher : IPublisher
		{
			public Int32 PublishCalled;
			public Message PublishArgument;


			public void Publish(Message message)
			{
				PublishArgument = message;
				PublishCalled++;
			}
		}

		private class MockProgressHandler
		{
			public Int32 ProgressArgument;
			public Int32 ProgressCalled;
			public AutoResetEvent ProgressSignal = new AutoResetEvent(false);

			public void HandleProgress(Int32 value)
			{
				ProgressArgument = value;
				ProgressCalled++;
				ProgressSignal.Set();
			}

			public void Wait()
			{
				Assert.That(ProgressSignal.WaitOne(5000), Is.True);
			}
		}


		// --- TESTS ---


		#region Publish(Object, Message)

		[Test]
		public void Test_Publish_Label_Message()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			message.Label = "ABC";
			publisher.Publish("LABEL", message);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("LABEL"));
		}

		[Test]
		public void Test_Publish_Label_Message_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.Publish("LABEL", message);
				});
		}

		[Test]
		public void Test_Publish_Label_Message_NullLabel()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.Publish(null, message);
				});
		}

		[Test]
		public void Test_Publish_Label_Message_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.Publish("LABEL", null);
				});
		}

		#endregion


		#region PublishAfter(TimeSpan, Message)

		[Test]
		public void Test_PublishAfter_TimeSpan_Message()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				message);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(0),
				message);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(-100),
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						null);
				});
		}

		#endregion

		#region PublishAfter(Int32, Message)

		[Test]
		public void Test_PublishAfter_Int32_Message()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			var task = publisher.PublishAfter(
				50,
				message);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			var task = publisher.PublishAfter(
				0,
				message);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						50,
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						-100,
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						50,
						null);
				});
		}

		#endregion

		#region PublishAfter(TimeSpan, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			source.Cancel();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_Cancelled_After_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));

			source.Cancel();
			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_Cancelled_After_Published()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));

			source.Cancel();

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(0),
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_ZeroDelay_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			source.Cancel();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(0),
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_NullPublisher_Cancelled_Before_Publish()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					source.Cancel();

					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(0),
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(-100),
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Message_CancellationToken_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						null);
				});
		}

		#endregion

		#region PublishAfter(Int32, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				50,
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			source.Cancel();

			var task = publisher.PublishAfter(
				50,
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_Cancelled_After_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				50,
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));

			source.Cancel();
			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_Cancelled_After_Published()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				50,
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));

			source.Cancel();

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			var task = publisher.PublishAfter(
				0,
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_ZeroDelay_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			source.Cancel();

			var task = publisher.PublishAfter(
				0,
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo(null));
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						50,
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_NullPublisher_Cancelled_Before_Publish()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					source.Cancel();

					publisher.PublishAfter(
						0,
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						-100,
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Message_CancellationToken_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						50,
						null);
				});
		}

		#endregion


		#region PublishAfter(TimeSpan, Object, Message)

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				"COM1",
				message);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(0),
				"COM1",
				message);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						"COM1",
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(-100),
						"COM1",
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_NullLabel()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						null,
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						"COM1",
						null);
				});
		}

		#endregion

		#region PublishAfter(Int32, Object, Message)

		[Test]
		public void Test_PublishAfter_Int32_Object_Message()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				50,
				"COM1",
				message);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				0,
				"COM1",
				message);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						50,
						"COM1",
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						-100,
						"COM1",
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_NullLabel()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						50,
						null,
						message);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						50,
						"COM1",
						null);
				});
		}

		#endregion

		#region PublishAfter(TimeSpan, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			source.Cancel();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_Cancelled_After_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			source.Cancel();
			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_Cancelled_After_Published()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(50),
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));

			source.Cancel();

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(0),
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_ZeroDelay_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			source.Cancel();

			var task = publisher.PublishAfter(
				TimeSpan.FromMilliseconds(0),
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						"COM1",
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_NullPublisher_Cancelled_Before_Publish()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					source.Cancel();

					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(0),
						"COM1",
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(-100),
						"COM1",
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_NullLabel()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						null,
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_TimeSpan_Object_Message_CancellationToken_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						TimeSpan.FromMilliseconds(50),
						"COM1",
						null);
				});
		}

		#endregion

		#region PublishAfter(Int32, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				50,
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			source.Cancel();

			var task = publisher.PublishAfter(
				50,
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_Cancelled_After_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				50,
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			source.Cancel();
			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_Cancelled_After_Published()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				50,
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.False);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));

			task.WaitQuietly(500);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));

			source.Cancel();

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_ZeroDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishAfter(
				0,
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.False);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_ZeroDelay_Cancelled_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			source.Cancel();

			var task = publisher.PublishAfter(
				0,
				"COM1",
				message,
				source.Token);

			Assert.That(task.IsCompleted, Is.True);
			Assert.That(task.IsCanceled, Is.True);
			Assert.That(task.IsFaulted, Is.False);
			Assert.That(publisher.PublishCalled, Is.EqualTo(0));
			Assert.That(publisher.PublishArgument, Is.EqualTo(null));
			Assert.That(message.Label, Is.EqualTo("NULL"));
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishAfter(
						50,
						"COM1",
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_NullPublisher_Cancelled_Before_Publish()
		{
			var publisher = (IPublisher)null;
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					source.Cancel();

					publisher.PublishAfter(
						0,
						"COM1",
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_NegativeDelay()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					publisher.PublishAfter(
						-100,
						"COM1",
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_NullLabel()
		{
			var publisher = new MockPublisher();
			var message = new TestMessage();
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						50,
						null,
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishAfter_Int32_Object_Message_CancellationToken_NullMessage()
		{
			var publisher = new MockPublisher();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishAfter(
						50,
						"COM1",
						null);
				});
		}

		#endregion


		#region PublishOperation(Message)

		[Test]
		public void Test_PublishOperation_Message()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();

			var task = publisher.PublishOperation(
				message);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.Null);
			Assert.That(task, Is.SameAs(message.Completion));
		}

		[Test]
		public void Test_PublishOperation_Message_Progress()
		{
			var handler = new MockProgressHandler();
			var publisher = new MockPublisher();
			var message = new TestOperation();

			var task = publisher.PublishOperation(
				message,
				handler.HandleProgress);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.Null);
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(handler.ProgressCalled, Is.EqualTo(0));
			Assert.That(handler.ProgressArgument, Is.EqualTo(0));

			message.ReportProgress(123);
			handler.Wait();

			Assert.That(handler.ProgressCalled, Is.EqualTo(1));
			Assert.That(handler.ProgressArgument, Is.EqualTo(123));
		}

		[Test]
		public void Test_PublishOperation_Message_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestOperation();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishOperation(
						message);
				});
		}

		[Test]
		public void Test_PublishOperation_Message_NullMessage()
		{
			var publisher = new MockPublisher();
			var message = (TestOperation)null;

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishOperation(
						message);
				});
		}

		#endregion

		#region PublishOperation(Message, CancellationToken)

		[Test]
		public void Test_PublishOperation_Message_CancellationToken()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			var task = publisher.PublishOperation(
				message,
				source.Token);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.Null);
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.False);
		}

		[Test]
		public void Test_PublishOperation_Message_CancellationToken_Cancel_After_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			var task = publisher.PublishOperation(
				message,
				source.Token);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.Null);
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.False);

			source.Cancel();

			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.True);
		}

		[Test]
		public void Test_PublishOperation_Message_CancellationToken_Cancel_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			source.Cancel();

			var task = publisher.PublishOperation(
				message,
				source.Token);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.Null);
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.True);
		}

		[Test]
		public void Test_PublishOperation_Message_CancellationToken_Progress()
		{
			var handler = new MockProgressHandler();
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			var task = publisher.PublishOperation(
				message,
				source.Token,
				handler.HandleProgress);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.Null);
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.False);
			Assert.That(handler.ProgressCalled, Is.EqualTo(0));
			Assert.That(handler.ProgressArgument, Is.EqualTo(0));

			message.ReportProgress(123);
			handler.Wait();

			Assert.That(handler.ProgressCalled, Is.EqualTo(1));
			Assert.That(handler.ProgressArgument, Is.EqualTo(123));
		}

		[Test]
		public void Test_PublishOperation_Message_CancellationToken_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishOperation(
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishOperation_Message_CancellationToken_NullMessage()
		{
			var publisher = new MockPublisher();
			var message = (TestOperation)null;
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishOperation(
						message,
						source.Token);
				});
		}

		#endregion

		#region PublishOperation(Object, Message)

		[Test]
		public void Test_PublishOperation_Object_Message()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();

			message.Label = "NULL";

			var task = publisher.PublishOperation(
				"COM1",
				message);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
			Assert.That(task, Is.SameAs(message.Completion));
		}

		[Test]
		public void Test_PublishOperation_Object_Message_Progress()
		{
			var handler = new MockProgressHandler();
			var publisher = new MockPublisher();
			var message = new TestOperation();

			message.Label = "NULL";

			var task = publisher.PublishOperation(
				"COM1",
				message,
				handler.HandleProgress);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(handler.ProgressCalled, Is.EqualTo(0));
			Assert.That(handler.ProgressArgument, Is.EqualTo(0));

			message.ReportProgress(123);
			handler.Wait();

			Assert.That(handler.ProgressCalled, Is.EqualTo(1));
			Assert.That(handler.ProgressArgument, Is.EqualTo(123));
		}

		[Test]
		public void Test_PublishOperation_Object_Message_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestOperation();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishOperation(
						"COM1",
						message);
				});
		}

		[Test]
		public void Test_PublishOperation_Object_Message_NullLabel()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishOperation(
						null,
						message);
				});
		}

		[Test]
		public void Test_PublishOperation_Object_Message_NullMessage()
		{
			var publisher = new MockPublisher();
			var message = (TestOperation)null;

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishOperation(
						"COM1",
						message);
				});
		}

		#endregion

		#region PublishOperation(Object, Message, CancellationToken)

		[Test]
		public void Test_PublishOperation_Object_Message_CancellationToken()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishOperation(
				"COM1",
				message,
				source.Token);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.False);
		}

		[Test]
		public void Test_PublishOperation_Object_Message_CancellationToken_Cancel_After_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishOperation(
				"COM1",
				message,
				source.Token);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.False);

			source.Cancel();

			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.True);
		}

		[Test]
		public void Test_PublishOperation_Object_Message_CancellationToken_Cancel_Before_Publish()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			source.Cancel();

			var task = publisher.PublishOperation(
				"COM1",
				message,
				source.Token);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.True);
		}

		[Test]
		public void Test_PublishOperation_Object_Message_CancellationToken_Progress()
		{
			var handler = new MockProgressHandler();
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			message.Label = "NULL";

			var task = publisher.PublishOperation(
				"COM1",
				message,
				source.Token,
				handler.HandleProgress);

			Assert.That(publisher.PublishCalled, Is.EqualTo(1));
			Assert.That(publisher.PublishArgument, Is.EqualTo(message));
			Assert.That(message.Label, Is.EqualTo("COM1"));
			Assert.That(task, Is.SameAs(message.Completion));
			Assert.That(message.CancellationToken.CanBeCanceled, Is.True);
			Assert.That(message.CancellationToken.IsCancellationRequested, Is.False);
			Assert.That(handler.ProgressCalled, Is.EqualTo(0));
			Assert.That(handler.ProgressArgument, Is.EqualTo(0));

			message.ReportProgress(123);
			handler.Wait();

			Assert.That(handler.ProgressCalled, Is.EqualTo(1));
			Assert.That(handler.ProgressArgument, Is.EqualTo(123));
		}

		[Test]
		public void Test_PublishOperation_Object_Message_CancellationToken_NullPublisher()
		{
			var publisher = (IPublisher)null;
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			Assert.Throws<NullReferenceException>(() =>
				{
					publisher.PublishOperation(
						"COM1",
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishOperation_Object_Message_CancellationToken_NullLabel()
		{
			var publisher = new MockPublisher();
			var message = new TestOperation();
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishOperation(
						null,
						message,
						source.Token);
				});
		}

		[Test]
		public void Test_PublishOperation_Object_Message_CancellationToken_NullMessage()
		{
			var publisher = new MockPublisher();
			var message = (TestOperation)null;
			var source = new CancellationTokenSource();

			Assert.Throws<ArgumentNullException>(() =>
				{
					publisher.PublishOperation(
						"COM1",
						message,
						source.Token);
				});
		}

		#endregion

	}
}

#endif
